From bd06237d842c9a2c57905f42a7ccb5f085b9ea12 Mon Sep 17 00:00:00 2001
From: Tobias Schramm <t.schramm@manjaro.org>
Date: Thu, 28 May 2020 14:06:20 +0200
Subject: [PATCH 01/22] soc: rockchip: Add rockchip suspend mode driver

Code gore, do not mainline. This belongs in ATF

Signed-off-by: Tobias Schramm <t.schramm@manjaro.org>
---
 .../soc/rockchip/rockchip-pm-config.txt       |  39 ++++
 drivers/soc/rockchip/Kconfig                  |   6 +
 drivers/soc/rockchip/Makefile                 |   1 +
 drivers/soc/rockchip/rockchip_pm_config.c     | 212 ++++++++++++++++++
 include/dt-bindings/suspend/rockchip-rk3399.h |  60 +++++
 5 files changed, 318 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
 create mode 100644 drivers/soc/rockchip/rockchip_pm_config.c
 create mode 100644 include/dt-bindings/suspend/rockchip-rk3399.h

diff --git a/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
new file mode 100644
index 000000000000..a8fd70f17597
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt
@@ -0,0 +1,39 @@
+* the suspend mode config
+
+- compatible: "rockchip,pm-config"
+  Compatibility with rk3399
+
+- rockchip,sleep-mode-config : the sleep mode config,
+  ARMOFF, OSC disabled ...
+
+- rockchip,wakeup-config: the wake up sourece enable.
+  GPIO, USB, SD...
+
+- rockchip,pwm-regulator-config: the pwm regulator name.
+
+Example:
+	rockchip_suspend: rockchip_suspend {
+		compatible = "rockchip,pm-rk3399";
+		status = "okay";
+		rockchip,sleep-mode-config = <
+			(0
+			| RKPM_SLP_ARMPD
+			| RKPM_SLP_PERILPPD
+			| RKPM_SLP_DDR_RET
+			| RKPM_SLP_PLLPD
+			| RKPM_SLP_OSC_DIS
+			| RKPM_SLP_CENTER_PD
+			| RKPM_SLP_AP_PWROFF
+			)
+		>;
+		rockchip,wakeup-config = <
+			(0 |
+			RKPM_GPIO_WKUP_EN |
+			RKPM_PWM_WKUP_EN)
+		>;
+		rockchip,pwm-regulator-config = <
+			(0 |
+			PWM2_REGULATOR_EN
+			)
+		>;
+	};
diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig
index 2c13bf4dd5db..f403ab803b42 100644
--- a/drivers/soc/rockchip/Kconfig
+++ b/drivers/soc/rockchip/Kconfig
@@ -34,4 +34,10 @@ config ROCKCHIP_PM_DOMAINS
 
           If unsure, say N.
 
+config ROCKCHIP_SUSPEND_MODE
+	bool "Rockchip suspend mode config"
+	depends on ROCKCHIP_SIP
+	help
+	  Say Y here if you want to set the suspend mode to the ATF.
+
 endif
diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile
index 875032f7344e..7aa3d5d1b330 100644
--- a/drivers/soc/rockchip/Makefile
+++ b/drivers/soc/rockchip/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_ROCKCHIP_GRF) += grf.o
 obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o
 obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o
+obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o
diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c
new file mode 100644
index 000000000000..43b2e0f33343
--- /dev/null
+++ b/drivers/soc/rockchip/rockchip_pm_config.c
@@ -0,0 +1,212 @@
+/*
+ * Rockchip Generic power configuration support.
+ *
+ * Copyright (c) 2017 ROCKCHIP, Co. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/rockchip/rockchip_sip.h>
+#include <linux/suspend.h>
+#include <dt-bindings/input/input.h>
+
+#define PM_INVALID_GPIO	0xffff
+
+static const struct of_device_id pm_match_table[] = {
+	{ .compatible = "rockchip,pm-rk3399",},
+	{ },
+};
+
+#define MAX_PWRKEY_NUMS		20
+#define MAX_NUM_KEYS		60
+
+struct rkxx_remote_key_table {
+	int scancode;
+	int keycode;
+};
+
+static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey)
+{
+	struct device_node *node;
+	struct device_node *child_node;
+	struct rkxx_remote_key_table key_table[MAX_NUM_KEYS];
+	int i;
+	int len = 0, nbuttons;
+	int num = 0;
+	u32 usercode, scancode;
+
+	for_each_node_by_name(node, "pwm") {
+		for_each_child_of_node(node, child_node) {
+			if (of_property_read_u32(child_node,
+						 "rockchip,usercode",
+						 &usercode))
+				break;
+
+			if (of_get_property(child_node,
+					    "rockchip,key_table",
+					    &len) == NULL ||
+			    len <= 0)
+				break;
+
+			len = len < sizeof(key_table) ? len : sizeof(key_table);
+			len /= sizeof(u32);
+			if (of_property_read_u32_array(child_node,
+						       "rockchip,key_table",
+						       (u32 *)key_table,
+						       len))
+				break;
+
+			nbuttons = len / 2;
+			for (i = 0; i < nbuttons && num < size; ++i) {
+				if (key_table[i].keycode == KEY_POWER) {
+					scancode = key_table[i].scancode;
+					pr_debug("usercode=%x, key=%x\n",
+						 usercode, scancode);
+					pwrkey[num] = (usercode & 0xffff) << 16;
+					pwrkey[num] |= (scancode & 0xff) << 8;
+					++num;
+				}
+			}
+		}
+	}
+
+	*nkey = num;
+
+	return num ? 0 : -1;
+}
+
+static void rockchip_pm_virt_pwroff_prepare(void)
+{
+	int error;
+	int i, nkey;
+	u32 power_key[MAX_PWRKEY_NUMS];
+
+	if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) {
+		pr_err("Parse ir powerkey code failed!\n");
+		return;
+	}
+
+	for (i = 0; i < nkey; ++i)
+		sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]);
+
+	regulator_suspend_prepare(PM_SUSPEND_MEM);
+
+	error = disable_nonboot_cpus();
+	if (error) {
+		pr_err("Disable nonboot cpus failed!\n");
+		return;
+	}
+
+	sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
+	sip_smc_virtual_poweroff();
+}
+
+static int __init pm_config_init(struct platform_device *pdev)
+{
+	const struct of_device_id *match_id;
+	struct device_node *node;
+	u32 mode_config = 0;
+	u32 wakeup_config = 0;
+	u32 pwm_regulator_config = 0;
+	int gpio_temp[10];
+	u32 sleep_debug_en = 0;
+	u32 apios_suspend = 0;
+	u32 virtual_poweroff_en = 0;
+	enum of_gpio_flags flags;
+	int i = 0;
+	int length;
+
+	match_id = of_match_node(pm_match_table, pdev->dev.of_node);
+	if (!match_id)
+		return -ENODEV;
+
+	node = of_find_node_by_name(NULL, "rockchip-suspend");
+
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(&pdev->dev, "%s dev node err\n",  __func__);
+		return -ENODEV;
+	}
+
+	if (of_property_read_u32_array(node,
+				       "rockchip,sleep-mode-config",
+				       &mode_config, 1))
+		dev_warn(&pdev->dev, "not set sleep mode config\n");
+	else
+		sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0);
+
+	if (of_property_read_u32_array(node,
+				       "rockchip,wakeup-config",
+				       &wakeup_config, 1))
+		dev_warn(&pdev->dev, "not set wakeup-config\n");
+	else
+		sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0);
+
+	if (of_property_read_u32_array(node,
+				       "rockchip,pwm-regulator-config",
+				       &pwm_regulator_config, 1))
+		dev_warn(&pdev->dev, "not set pwm-regulator-config\n");
+	else
+		sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG,
+					 pwm_regulator_config,
+					 0);
+
+	length = of_gpio_named_count(node, "rockchip,power-ctrl");
+
+	if (length > 0 && length < 10) {
+		for (i = 0; i < length; i++) {
+			gpio_temp[i] = of_get_named_gpio_flags(node,
+							     "rockchip,power-ctrl",
+							     i,
+							     &flags);
+			if (!gpio_is_valid(gpio_temp[i]))
+				break;
+			sip_smc_set_suspend_mode(GPIO_POWER_CONFIG,
+						 i,
+						 gpio_temp[i]);
+		}
+	}
+	sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO);
+
+	if (!of_property_read_u32_array(node,
+					"rockchip,sleep-debug-en",
+					&sleep_debug_en, 1))
+		sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE,
+					 sleep_debug_en,
+					 0);
+
+	if (!of_property_read_u32_array(node,
+					"rockchip,apios-suspend",
+					&apios_suspend, 1))
+		sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG,
+					 apios_suspend,
+					 0);
+
+	if (!of_property_read_u32_array(node,
+					"rockchip,virtual-poweroff",
+					&virtual_poweroff_en, 1) &&
+	    virtual_poweroff_en)
+		pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
+
+	return 0;
+}
+
+static struct platform_driver pm_driver = {
+	.driver = {
+		.name = "rockchip-pm",
+		.of_match_table = pm_match_table,
+	},
+};
+
+static int __init rockchip_pm_drv_register(void)
+{
+	return platform_driver_probe(&pm_driver, pm_config_init);
+}
+subsys_initcall(rockchip_pm_drv_register);
diff --git a/include/dt-bindings/suspend/rockchip-rk3399.h b/include/dt-bindings/suspend/rockchip-rk3399.h
new file mode 100644
index 000000000000..0cccd6430ef6
--- /dev/null
+++ b/include/dt-bindings/suspend/rockchip-rk3399.h
@@ -0,0 +1,60 @@
+/*
+ * Header providing constants for Rockchip suspend bindings.
+ *
+ * Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Tony.Xie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__
+#define __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__
+
+/* the suspend mode */
+#define RKPM_SLP_WFI				(1 << 0)
+#define RKPM_SLP_ARMPD				(1 << 1)
+#define RKPM_SLP_PERILPPD			(1 << 2)
+#define RKPM_SLP_DDR_RET			(1 << 3)
+#define RKPM_SLP_PLLPD				(1 << 4)
+#define RKPM_SLP_OSC_DIS			(1 << 5)
+#define RKPM_SLP_CENTER_PD			(1 << 6)
+#define RKPM_SLP_AP_PWROFF			(1 << 7)
+
+/* the wake up source */
+#define RKPM_CLUSTER_L_WKUP_EN			(1 << 0)
+#define RKPM_CLUSTER_B_WKUPB_EN			(1 << 1)
+#define RKPM_GPIO_WKUP_EN			(1 << 2)
+#define RKPM_SDIO_WKUP_EN			(1 << 3)
+#define RKPM_SDMMC_WKUP_EN			(1 << 4)
+#define RKPM_TIMER_WKUP_EN			(1 << 6)
+#define RKPM_USB_WKUP_EN			(1 << 7)
+#define RKPM_SFT_WKUP_EN			(1 << 8)
+#define RKPM_WDT_M0_WKUP_EN			(1 << 9)
+#define RKPM_TIME_OUT_WKUP_EN			(1 << 10)
+#define RKPM_PWM_WKUP_EN			(1 << 11)
+#define RKPM_PCIE_WKUP_EN			(1 << 13)
+
+/* the pwm regulator */
+#define PWM0_REGULATOR_EN			(1 << 0)
+#define PWM1_REGULATOR_EN			(1 << 1)
+#define PWM2_REGULATOR_EN			(1 << 2)
+#define PWM3A_REGULATOR_EN			(1 << 3)
+#define PWM3B_REGULATOR_EN			(1 << 4)
+
+/* the APIO voltage domain */
+#define RKPM_APIO0_SUSPEND			(1 << 0)
+#define RKPM_APIO1_SUSPEND			(1 << 1)
+#define RKPM_APIO2_SUSPEND			(1 << 2)
+#define RKPM_APIO3_SUSPEND			(1 << 3)
+#define RKPM_APIO4_SUSPEND			(1 << 4)
+#define RKPM_APIO5_SUSPEND			(1 << 5)
+
+#endif
-- 
2.30.0

